package com.jeasonzhao.report.engine.impl;

import com.jeasonzhao.commons.json.JSONArray;
import com.jeasonzhao.commons.json.JSONConstant;
import com.jeasonzhao.commons.json.JSONException;
import com.jeasonzhao.commons.json.JSONNode;
import com.jeasonzhao.commons.json.JSONNodeTypes;
import com.jeasonzhao.commons.parser.expression.EvalException;
import com.jeasonzhao.commons.parser.expression.ExpressionParser;
import com.jeasonzhao.commons.parser.expression.SyntaxException;
import com.jeasonzhao.commons.parser.expression.SyntaxNode;
import com.jeasonzhao.commons.parser.expression.ValuePair;
import com.jeasonzhao.commons.parser.lex.LexException;
import com.jeasonzhao.commons.utils.Algorithms;
import com.jeasonzhao.report.engine.ReportUserInfo;
import com.jeasonzhao.report.engine.irp.IParameterValueCollector;
import com.jeasonzhao.report.model.ObjectCollection;
import com.jeasonzhao.report.model.Report;
import com.jeasonzhao.report.model.ReportParam;
import com.jeasonzhao.commons.json.JSONClass;
import com.jeasonzhao.commons.basic.StringPairCollection;
import com.jeasonzhao.report.dataset.DataSetFilter;
import com.jeasonzhao.commons.basic.StringPair;
import com.jeasonzhao.report.exceptions.ReportParameterException;
import com.jeasonzhao.report.exceptions.SQLReportException;

public abstract class AbstractParameterValueCollector implements IParameterValueCollector
{
    public Report setupReport()
        throws SQLReportException
    {
        return null;
    }

    public int getPageSize(Report report)
        throws SQLReportException
    {
        return null == report ? -1 : report.getPageSize();
    }

    public int getPageNo(Report report)
        throws SQLReportException
    {
        return 0;
    }

    public String getRenderKey()
    {
        return null;
    }

    public void collectDynQuerySettings(Report report)
        throws SQLReportException
    {
    }

    protected void collectDynQuerySettingsFromJSONString(Report report,String strJSON)
        throws JSONException
    {
        if(Algorithms.isEmpty(strJSON) || null == report)
        {
            return;
        }
        JSONClass cls = JSONClass.from(strJSON);
        JSONArray ary = (JSONArray) cls.getNode("filters","filter");
        java.util.Vector<DataSetFilter> fis = new java.util.Vector<DataSetFilter>();
        for(int n = 0;null != ary && n < ary.size();n++)
        {
            JSONClass item = (JSONClass) ary.getItem(n);
            DataSetFilter filter = DataSetFilter.fromJSON(item);
            if(filter != null)
            {
                fis.addElement(filter);
            }
        }
        StringPairCollection sorts = new StringPairCollection();
        ary = (JSONArray) cls.getNode("sorts");
        for(int n = 0;null != ary && n < ary.size();n++)
        {
            JSONClass item = (JSONClass) ary.getItem(n);
            sorts.add(new StringPair(
                item.valueOf("colGuid","guid","column","col","id")
                ,item.valueOf("sortMode","value","mode","model")));
        }
        report.setSortSettings(sorts);
        report.setDataSetFilters(fis);

    }

    public void tearDown(Report report)
    {
    }

    public boolean collectParameters(Report report,ReportUserInfo user)
        throws ReportParameterException
    {
        if(null == report)
        {
            return false;
        }
        if(report.getParametersCount() <= 0)
        {
            return true;
        }
        for(ReportParam parameter : report.getParameters())
        {
            if(parameter == null) // No handler for null parameter
            {
                continue;
            }
            parameter.clearValues(); // Clear values before executing
            if(parameter.isFixedValue()) // Fixed value means we should ignore
            // what users input the value of
            // this parameter.
            {
                if(Algorithms.isEmpty(parameter.getDefaultValueScript()))
                {
                    throw new ReportParameterException.ParameterScriptNeeded(report,parameter);
                }
                if(generateDefaultValue(report,user,parameter) == false)
                {
                    throw new ReportParameterException.ParameterNotSet(report,parameter);
                }
            }
            else
            // OK, first, try to get parameter value and then check whether the
            // parameter is optional or not.
            {
                if(false == getParameterValue(report,user,parameter))
                {
                    if(false == parameter.isOptional())
                    {
                        if(generateDefaultValue(report,user,parameter) == false)
                        {
                            throw new ReportParameterException.ParameterNotSet(report,parameter);
                        }
                    }
                    else
                    {
                        // No matter the value has been set or not
                        generateDefaultValue(report,user,parameter);
                    }
                }
            }
        }
        return true;
    }

    protected boolean generateDefaultValue(Report report,ReportUserInfo user,ReportParam parameter)
        throws ReportParameterException
    {
        if(null == parameter)
        {
            return false;
        }
        String defaultValueScript = parameter.getDefaultValueScript();
        try
        {
            ObjectCollection objcoll = parseExpression(user,defaultValueScript);
            if(null != objcoll && objcoll.size() > 0)
            {
                parameter.setValue(objcoll);
                return true;
            }
            else
            {
                return false;
            }
        }
        catch(EvalException e)
        {
            throw new ReportParameterException.ParamterScriptError(report,parameter,defaultValueScript,e);
        }
        catch(SyntaxException e)
        {
            throw new ReportParameterException.ParamterScriptError(report,parameter,defaultValueScript,e);
        }
        catch(LexException e)
        {
            throw new ReportParameterException.ParamterScriptError(report,parameter,defaultValueScript,e);
        }
        catch(JSONException e)
        {
            throw new ReportParameterException.ParamterScriptError(report,parameter,defaultValueScript,e);
        }
    }

    protected abstract boolean getParameterValue(Report report,ReportUserInfo user,ReportParam parameter)
        throws ReportParameterException;

    private ObjectCollection parseExpression(ReportUserInfo user,String strScript)
        throws EvalException,SyntaxException,LexException,JSONException
    {
        if(Algorithms.isEmpty(strScript))
        {
            return null;
        }
        strScript = strScript.trim();
        if(strScript.startsWith("="))
        {
            ExpressionParser parser = new ExpressionParser();
            parser.setExpression(strScript);
            SyntaxNode node = parser.parseNode();
            ValuePair returnValue = node.eval(new UserEvalProvider(user));

            Object obj = returnValue.getValue();
            if(null != obj)
            {
                ObjectCollection coll = new ObjectCollection();
                coll.add(obj);
                return coll;
            }
            else
            {
                return null;
            }
        }
        else
        if(strScript.startsWith("[") && strScript.endsWith("]"))
        {
            ObjectCollection coll = new ObjectCollection();
            for(JSONNode node : JSONArray.from(strScript).items())
            {
                if(node.getNodeType() != JSONNodeTypes.CONSTANT)
                {
                    throw new JSONException("Non-Constant is not allowed here");
                }
                ObjectCollection subvaluesx = parseExpression(user,((JSONConstant) node).getValue());
                if(subvaluesx != null)
                {
                    coll.addAll(subvaluesx);
                }
            }
            return coll.size() < 1 ? null : coll;
        }
        else
        {
            ObjectCollection coll = new ObjectCollection();
            coll.add(strScript);
            return coll;
        }
    }

}
